/*
 * protection.cpp
 *
 *  Created on: 20.07.2013
 *      Author: SpirTanol
 */

#include "device/protection.h"
#include "packets/config_packet.h"

Protection::Protection()
{
	prg_fast_h=10645;//!< верхний порог для быстродействующей защиты
	Tm=1;//!< тепловая постоянная двигателя
	Qm=0;//нормированная тепловая энергия двигателя
	u8 counter=0;
}

Protection::~Protection()
{

}

void Protection::setConfig(const ConfigPacket *config)
{
	ust.nom_current = config->rawCurNom();
	ust.min_current = u16(config->curMin());
	ust.max_current = u16(config->curOverload());
	ust.otkl_t2 = u16(config->offTimeDoubleOverload());
	ust.fast_prt_on = u8(config->fastProtect() ? 1 : 0);
	ust.max_c_time = u16(config->protectTime());
	ust.obr = u16(config->countCrashPhases());
}

void Protection::copyConfig(const Protection *protection)
{
	const USTAVK &prot_ust = protection->ust;

	ust.nom_current = prot_ust.nom_current;
	ust.min_current = prot_ust.min_current;
	ust.max_current = prot_ust.max_current;
	ust.otkl_t2 = prot_ust.otkl_t2;
	ust.fast_prt_on = prot_ust.fast_prt_on;
	ust.max_c_time = prot_ust.max_c_time;
	ust.obr = prot_ust.obr;
}

void Protection::avar_off(void)
{
 tripping=1;
}

void Protection::protect_one_phase_I(s16 cr_norm)
{
 if (!ust.fast_prt_on) return;

  if (cr_norm>=prg_fast_h)
   {
    avar_off();
   }
}

//! однофазная токовая защита, участок R

void Protection::protect_one_phase_R(u8 ch, u16 cr_norm)
{
 overld[ch] = cr_norm>ust.max_current*100;

 if (ch==2)
   {
    if (overld[0]||overld[1]||overld[2])
      {
       if (++overld_time>=ust.max_c_time/10)
         {
          avar_off();
          overld_time=0;
         }
      }
     else
      if (overld_time) --overld_time;
   }
}

//! однофазная токовая защита, участок U (недогруз)
void Protection::protect_one_phase_U(u8 ch, u16 cr_norm_x10)
{
if (ch>2) return;

   if ((cr_norm_x10<=ust.min_current*10)&&(run))
     {
      if (counter_u[ch]<255) counter_u[ch]++; // обратный отсчет до отключения
     }
    else if (counter_u[ch]) counter_u[ch]--;// "интегрирование", если перекос на грани допустимого

u8 obr_ch_cnt=0;
//ждем X циклов и считаем оборванные фазы
for(u8 i = 0;i < 3;i++)
if (counter_u[i]>=100)
  {
   obr_ch_cnt++;
  }

   if (obr_ch_cnt>ust.obr)
     {
      avar_off();
     }
}

//! псевдотепловая защита
void Protection::protect_one_phase_L(u16 cr_norm_x10)//однофазная токовая защита, участок L
{
 float dQ,N;//полученная или отданная двигателем энергия

// static u32 tick_counter=0;

 #define DT 0.01f //dt=0.01sec  длительность цикла

 //рассчет кратности тока
 N=cr_norm_x10/1000.0;

 //расчет полученной или отданной двигателем энергии
 dQ=((N*N-Qm)*DT)/Tm;
 Qm+=dQ;

 if (Qm>1.1011f)//движок перегрелся, отключаемся (1.1011 равновесное состояние при I=1.05In)
   {
    avar_off();
   }
}




//! проверка, работает ли двигатель
void Protection::check_run(u8 ch, u16 cr_norm_x10)
{
 u8 rn=0;

 if (ch>2) return;
 currents[ch]=cr_norm_x10;

#define prg  (150)     // 15%
#define prg2 (prg*4/5) // /2
#define prg3 (prg*3/5) // /3

#define hyst  (2) // гистерезис 0.2%

#define prg_  (prg-hyst)  // 15%
#define prg2_ (prg2-hyst) // /2
#define prg3_ (prg3-hyst) // /3

 if (ch==2)
   {
    //проверка: а не подключена ли нагрузка?

    if (run)
      {
       if ((currents[0]>=prg3_)&&(currents[1]>=prg3_)&&(currents[2]>=prg3_)) rn=1;
       if ((currents[0]>=prg2_)&&(currents[1]>=prg2_)||(currents[1]>=prg2_)&&
           (currents[2]>=prg2_)||(currents[2]>=prg2_)&&(currents[0]>=prg2_)) rn=1;
       if ((currents[0]>=prg_ )||(currents[1]>=prg_) ||(currents[2]>=prg_)) rn=1;
      }
     else
      {
       if ((currents[0]>=prg3)&&(currents[1]>=prg3)&&(currents[2]>=prg3)) rn=1;
       if ((currents[0]>=prg2)&&(currents[1]>=prg2)||(currents[1]>=prg2)&&
           (currents[2]>=prg2)||(currents[2]>=prg2)&&(currents[0]>=prg2)) rn=1;
       if ((currents[0]>=prg )||(currents[1]>=prg) ||(currents[2]>=prg)) rn=1;
      }

    if (rn)
      {
       if (counter<20) counter++;
      }
    else if (counter) counter--;

    if (counter>10) run=1;
     else
    if (counter<10) run=0;
   }
}

//! вызов всех защит и соответствующих обработок данных для одной фазы
/*!(фазы чередуются)*/
void Protection::protect_one_phase(u8 ch, u16 cr_norm_x10)
{

 protect_one_phase_I(cr_norm_x10);
 protect_one_phase_R(ch,cr_norm_x10/10);

 protect_one_phase_L(cr_norm_x10);   // !! не нарушать последовательность

 check_run(ch,cr_norm_x10);       // сначала выяснить, работает ли двигатель

 protect_one_phase_U(ch,cr_norm_x10);// !! не нарушать последовательность
}

//!инициализация защит
void Protection::init_protection(void)
{
 run=0;

 //инициализация тепловой защиты
 Tm=ust.otkl_t2*9.32f;
 Qm=0;

 //обнуление вспомогательных данных
 overld_time=0;
 counter=0;
for(u8 i=0;i<3;i++)
  {
   overld[i]=0;
   currents[i]=0;
   counter_u[i]=0;
  }
}
u32 Protection::get_trip_time(u32 norm_current)
{
u32 time=0;
u8 ch=0;

 init_protection();
 tripping=0;
 do
  {
   protect_one_phase(ch++, norm_current);
   if (ch>2) ch=0;
   time+=3333;
  }
 while((!tripping) && (time<3600000000));

 return time;
}
